package naito_rescue.agent;

import java.util.*;
import java.io.*;
import java.net.*;
import rescuecore2.worldmodel.*;
import rescuecore2.messages.Command;
import rescuecore2.misc.Pair;
import rescuecore2.misc.geometry.*;
import rescuecore2.log.Logger;
import rescuecore2.standard.entities.*;
import rescuecore2.standard.messages.*;

import naito_rescue.*;
import naito_rescue.agent.*;
import naito_rescue.object.*;
import naito_rescue.router.*;
import naito_rescue.task.*;
import naito_rescue.task.job.*;
import naito_rescue.util.sort.*;
import naito_rescue.util.fb.*;
import naito_rescue.message.*;
import naito_rescue.message.manager.*;
//import naito_rescue.message.stream.*;

//naito_viewer.commmand.PaintRequestProcessor
//naito_viewer.command.PaintRequestCommand.Header;
import naito_viewer.PaintRequestProcessor;
import naito_viewer.CommandHeaderEnum;
import static naito_rescue.debug.DebugUtil.*;

/* Task-Jobを基にした行動設計 */
public class NAITOFireBrigade extends NAITOHumanoidAgent<FireBrigade>
{
	boolean debug = true;
	
	private ArrayList<Building> burningHearBuildings;
	private Random  seed;
	private List<EntityID> savedPath = null;
	private StandardEntity previousLocation = null;
	private int     sameLocationCount = 0;
	private Building savedTarget;
	
    protected void postConnect() {
        super.postConnect();
        model.indexClass(StandardEntityURN.BUILDING, StandardEntityURN.REFUGE);

		seed = new Random();
		if(!this.crowlingBuildings.isEmpty()){
			logger.info("crowlingBuildings shuffled. ", "FB#postConnect");
			Collections.shuffle(this.crowlingBuildings, seed);
		}
	}

	public String toString(){
		return "NAITOFireBrigade." + me().getID() + "";
	}

	protected void think(int time, ChangeSet changed, Collection<Command> heard){
		super.think(time, changed, heard);
		
		String context = "FB#think";
		long start,stop;
		logger.info("--------");
		start = System.currentTimeMillis();
		if (time < config.getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY)){
			return;
		}else if(time == config.getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY)){
			logger.info("Number of crowling-buildings = " + crowlingBuildings.size(), context);
			debugviewer.sendCommand(CommandHeaderEnum.PAINTEDGE, pfList.get(0).getID());
		}else if(time == 10){
			System.err.println("cancel pf");
			//debugviewer.sendCommand(PaintRequestCommand.Header.CANCEL, pfList.get(0).getID());
		}
		
		if(this.savedPath != null && !this.savedPath.isEmpty()){
			logger.info("(savedPath is not null) " + this.toString() + " is now moving.", context);
			EntityID goalID = this.savedPath.get(savedPath.size() - 1);
			if(getLocation().getID().getValue() != goalID.getValue()){
				// 自分の居る場所を得る
				EntityID locationID = getLocation().getID();
				EntityID nextPathID = null;
				logger.info("Current location = " + locationID, context);
				for(int i = 0;i < this.savedPath.size();i++){
					EntityID currentID = this.savedPath.get(i);
					if(currentID.getValue() == locationID.getValue()){
						nextPathID = this.savedPath.get(i + 1);
						break;
					}
				}
				//通行可否のロギング
				if(nextPathID != null){
					PassableChecker checker = search.getPassableChecker();
					if(!checker.isPassableTo(locationID, nextPathID)){
						logger.info("*** Need Re-planning, because there are some blockades. ***", context);
						logger.info("Current location ID = " + locationID.getValue(), context);
						logger.info("Next location ID    = " + nextPathID.getValue(), context);
						logger.info("Goal ID             = " + goalID.getValue(), context);
					}else{
						logger.info("*** Haven't Re-planning ***", context);
						logger.info("Current location ID = " + locationID.getValue(), context);
						logger.info("Next location ID    = " + nextPathID.getValue(), context);
						logger.info("Goal ID             = " + goalID.getValue(), context);
					}
				}else{
					logger.info("nextPathID is NULL.", context);
				}
			}else{
				logger.info(this.toString() + " is now goal.", context);
				logger.info("Current location = " + getLocation().getID() + ", goal = " + goalID, context);
			}
		}else{
			logger.info("(savedPath is null, or savedPath is empty) " + this.toString() + " is NOT moving.", context);
		}
		// 訪れた建物を削除
		StandardEntity location = getLocation();
		
		if(location instanceof Building){
			boolean isDeleted = crowlingBuildings.remove((Building)location);
			if(isDeleted){
				logger.info("Delete passed building: ID=" + location.getID(), context);
				logger.info("crowlingBuildings shuffled. ", context);
				Collections.shuffle(this.crowlingBuildings, seed);
				debugviewer.sendCommand(CommandHeaderEnum.CANCEL, location.getID());
			}
		}
		if(previousLocation != null && location.getID().getValue() == previousLocation.getID().getValue()){
			sameLocationCount++;
			if(sameLocationCount >= 2){
				logger.info(this.toString() + " has been same position 3 turns. target changes.", context);
				logger.info("Current target = " + crowlingBuildings.get(0), context);
				Collections.shuffle(this.crowlingBuildings, seed);
				logger.info("New target = " + crowlingBuildings.get(0), context);
			}
		}else{
			previousLocation = location;
			sameLocationCount = 0;
		}
		//-- Pathfinding --//
		//Collections.sort(crowlingBuildings, distanceSorter);
		Building target = crowlingBuildings.get(0);
		if(savedTarget == null){
			this.savedTarget = target;
		}else if(this.savedTarget.getID().getValue() != target.getID().getValue()){
			debugviewer.sendCommand(CommandHeaderEnum.CANCEL, savedTarget.getID());
			this.savedTarget = target;
		}
		String contextFromTo = "[" + location().getID() + " -> " + target.getID() + "]";
		logger.info("Path planning to " + target.getID().getValue(), contextFromTo);
		//List<EntityID> astarPath         = search.astarSearch(target);
		List<EntityID> astarBackwardPath = search.backwardAStarSearch(target);
		//List<EntityID> breadthFirstPath  = search.breadthFirstSearch(target);
		List<EntityID> aastarPath = search.adaptiveAStarSearch(target);
		// List<EntityID> astarPath2        = search.astarSearch2(target);
		
		//描画要請のテスト
		// | sender-id | command(1byte) | num-of-id(4byte) | id | ... | id | color-rgb(4byte) | boolean-value that paint id(1byte?) |
		debugviewer.sendCommand(CommandHeaderEnum.PAINTEDGE, target.getID());
		if(astarBackwardPath != null){
			// logger.info(contextFromTo + " A*(Backward) Path length = " + search.computePathLength(astarBackwardPath));
			// logger.trace(contextFromTo + " Path = " + astarBackwardPath);
		}
		if(aastarPath != null && !aastarPath.isEmpty()){
			EntityID goalID = aastarPath.get(aastarPath.size() - 1);
			commManager.accumulateMessage(new BlockedRoadMessage(goalID));
			commManager.flushMessages();
			move(aastarPath);
		}else{
			logger.info("Target building (" + target.getID() + ") is not reached.", context);
			Collections.shuffle(this.crowlingBuildings, seed);
			logger.info("New target building is \'" + crowlingBuildings.get(0) + "\'.", context);
		}
		this.savedPath = aastarPath;
		logger.info("--------\n");
	}
	protected boolean extinguishAroundHere(){
		List<Building> aroundHeres = new ArrayList<Building>();
		for(EntityID visibleID : this.changed.getChangedEntities()){
			if(model.getEntity(visibleID) instanceof Building){
				Building b = (Building)(model.getEntity(visibleID));
				int distance = model.getDistance(getLocation(), b);
				if(distance < this.maxExtinguishDistance){
					if(b.isOnFire()){
						aroundHeres.add(b);
					}else{
						//preExtinguish
						double temp = FireBrigadeUtils.ignitionLevel(getConfig(),b);
						if(b.isTemperatureDefined() && b.getTemperature() > temp){
							aroundHeres.add(b);
						}
					}
				}
			}
		}
		if(!aroundHeres.isEmpty()){
			//logger.info("Shoud extinguish around here. ", "NAITOFireBrigade#extinguishAroundHere");
			//優先度の降順にソートする
			Collections.sort(aroundHeres, ComparatorUtils.getBuildingPriorityComparator(this));
			Building target = aroundHeres.get(0);
			extinguish(target);
			return true;
		}
		return false;
	}
	/*
	protected void addTaskInExtinguishableRange(){
		//logger.info("** addTaskInExtinguishableRange; **");
		for(StandardEntity b : allBuildings){
			Building building = (Building)b;
			
			int distance = model.getDistance(getLocation(), building);
			if(building.isOnFire()){
				//currentTaskList.add(new ExtinguishTask(this, building));
				addTaskIfNew(new ExtinguishTask(this, building));
				//logger.info("Add ExtinguishTask In Range.:" + building);
			}
		}
		//logger.info("** addTaskInExtinguishableRange; end **");
	}
	*/
	protected void removeFinishedTask(){
		//logger.info("** removeFinishedTask(); **");
		for(Iterator<Task> it = currentTaskList.iterator();it.hasNext();){
			Task t = it.next();
			if(t.isFinished()){
				//logger.info("Remove finished task: " + t);
				it.remove();
			}
		}
		//logger.info("** removeFinishedTask(); end **");
	}
	protected void addTaskByMessage(List<NAITOMessage> receivedNow){
		//logger.info("** addTaskByMessage(); **");
		for(NAITOMessage m : receivedNow){
			if(m instanceof FireMessage){
				FireMessage fm = (FireMessage)m;
				List<EntityID> ids = fm.getIDs();
				for(EntityID id : ids){
					Building target = (Building)(model.getEntity(id));
					//currentTaskList.add(new ExtinguishTask(this, target));
					addTaskIfNew(new ExtinguishTask(this, target));
					//logger.info("Add ExtinguishTask By Message.:" + target, "addTaskByMessage");
				}
			}
		}
		//logger.info("** addTaskByMessage(); end **");
	}
	protected void updateTaskPriority(){
		Object[] tasks = currentTaskList.toArray();
		currentTaskList.clear();
		for(Object t : tasks){
			Task task = (Task)t;
			task.updatePriority();
			currentTaskList.add(task);
		}
	}
	protected EnumSet<StandardEntityURN> getRequestedEntityURNsEnum(){
		return EnumSet.of(StandardEntityURN.FIRE_BRIGADE);
	}

}
